/**
*@file  aht20.c
*@brief AHT20 Sensor Driver Implementation.
*This file contains the implementation of the AHT20 sensor driver, which is responsible for
*communicating with and controlling the AHT20 temperature and humidity sensor.
*The driver handles sensor initialization, data acquisition, and interaction with the device.
*@author Maker Studio
*@license Apache 2.0
*This code is open-sourced under the Apache 2.0 license. When using open-source libraries
*or code, please ensure to acknowledge the original authors and sources.
*/


#include "../inc/aht20.h"
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "iot_i2c.h"
#include "iot_errno.h"

#define AHT20_I2C_IDX 1
#define AHT20_STARTUP_TIME     20*1000
#define AHT20_CALIBRATION_TIME 40*1000
#define AHT20_MEASURE_TIME     75*1000
#define AHT20_DEVICE_ADDR   0x38
#define AHT20_CMD_CALIBRATION       0xBE
#define AHT20_CMD_CALIBRATION_ARG0  0x08
#define AHT20_CMD_CALIBRATION_ARG1  0x00
#define AHT20_CMD_TRIGGER       0xAC
#define AHT20_CMD_TRIGGER_ARG0  0x33
#define AHT20_CMD_TRIGGER_ARG1  0x00
#define AHT20_CMD_RESET      0xBA
#define AHT20_CMD_STATUS     0x71
#define AHT20_STATUS_BUSY_SHIFT 7
#define AHT20_STATUS_BUSY_MASK  (0x1<<AHT20_STATUS_BUSY_SHIFT)
#define AHT20_STATUS_BUSY(status) ((status & AHT20_STATUS_BUSY_MASK) >> AHT20_STATUS_BUSY_SHIFT)
#define AHT20_STATUS_MODE_SHIFT 5
#define AHT20_STATUS_MODE_MASK  (0x3<<AHT20_STATUS_MODE_SHIFT)
#define AHT20_STATUS_MODE(status) ((status & AHT20_STATUS_MODE_MASK) >> AHT20_STATUS_MODE_SHIFT)
#define AHT20_STATUS_CALI_SHIFT 3
#define AHT20_STATUS_CALI_MASK  (0x1<<AHT20_STATUS_CALI_SHIFT)
#define AHT20_STATUS_CALI(status) ((status & AHT20_STATUS_CALI_MASK) >> AHT20_STATUS_CALI_SHIFT)
#define AHT20_STATUS_RESPONSE_MAX 6
#define AHT20_RESLUTION            (1<<20)
#define AHT20_MAX_RETRY 10

/**
 * @brief AHT20 read function.
 *
 * This function is used to read data from the AHT20 sensor over I2C. The data is 
 * stored in the provided buffer.
 *
 * @param buffer The buffer to store the read data.
 * @param buffLen The length of the buffer.
 *
 * @return Returns IOT_SUCCESS if the operation is successful, otherwise returns 
 *         the error code.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
static uint32_t AHT20_Read(uint8_t *buffer, uint32_t buffLen)
{
    uint32_t retval = IoTI2cRead(AHT20_I2C_IDX, AHT20_DEVICE_ADDR, buffer, buffLen);
    if (retval != IOT_SUCCESS) {
        osal_printk("I2cRead() failed, %0X!\n", retval);
        return retval;
    }
    return IOT_SUCCESS;
}

/**
 * @brief AHT20 write function.
 *
 * This function is used to write data to the AHT20 sensor over I2C.
 *
 * @param buffer The buffer containing the data to be written.
 * @param buffLen The length of the data to be written.
 *
 * @return Returns IOT_SUCCESS if the operation is successful, otherwise returns 
 *         the error code.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
static uint32_t AHT20_Write(uint8_t *buffer, uint32_t buffLen)
{
    uint32_t retval = IoTI2cWrite(AHT20_I2C_IDX, AHT20_DEVICE_ADDR, buffer, buffLen);
    if (retval != IOT_SUCCESS) {
        osal_printk("I2cWrite() failed, %0X!\n", retval);
        return retval;
    }
    return IOT_SUCCESS;
}

/**
 * @brief Send AHT20 status command.
 *
 * This function sends a command to the AHT20 sensor to retrieve its status.
 *
 * @return Returns IOT_SUCCESS if the operation is successful, otherwise returns 
 *         the error code.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
static uint32_t AHT20_StatusCommand(void)
{
    uint8_t statusCmd[] = { AHT20_CMD_STATUS };
    return AHT20_Write(statusCmd, sizeof(statusCmd));
}

/**
 * @brief Send AHT20 reset command.
 *
 * This function sends a soft reset command to the AHT20 sensor to reset the sensor
 * without turning off the power.
 *
 * @return Returns IOT_SUCCESS if the operation is successful, otherwise returns 
 *         the error code.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
static uint32_t AHT20_ResetCommand(void)
{
    uint8_t resetCmd[] = {AHT20_CMD_RESET};
    return AHT20_Write(resetCmd, sizeof(resetCmd));
}

/**
 * @brief Send AHT20 calibration command.
 *
 * This function sends the calibration command to initialize and calibrate the AHT20 sensor.
 *
 * @return Returns IOT_SUCCESS if the operation is successful, otherwise returns 
 *         the error code.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
static uint32_t AHT20_CalibrateCommand(void)
{
    uint8_t clibrateCmd[] = {AHT20_CMD_CALIBRATION, AHT20_CMD_CALIBRATION_ARG0, AHT20_CMD_CALIBRATION_ARG1};
    return AHT20_Write(clibrateCmd, sizeof(clibrateCmd));
}

/**
 * @brief Send AHT20 measurement trigger command.
 *
 * This function triggers the AHT20 sensor to start a measurement.
 *
 * @return Returns IOT_SUCCESS if the operation is successful, otherwise returns 
 *         the error code.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
uint32_t AHT20_StartMeasure(void)
{
    uint8_t triggerCmd[] = {AHT20_CMD_TRIGGER, AHT20_CMD_TRIGGER_ARG0, AHT20_CMD_TRIGGER_ARG1};
    return AHT20_Write(triggerCmd, sizeof(triggerCmd));
}

/**
 * @brief AHT20 calibration process.
 *
 * This function checks the calibration status and sends a reset or calibration command
 * to the sensor if necessary.
 *
 * @return Returns IOT_SUCCESS if the operation is successful, otherwise returns 
 *         the error code.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
uint32_t AHT20_Calibrate(void)
{
    uint32_t retval = 0;
    uint8_t buffer[AHT20_STATUS_RESPONSE_MAX] = { 0 };
    memset(&buffer, 0x0, sizeof(buffer));

    retval = AHT20_StatusCommand();
    if (retval != IOT_SUCCESS) {
        return retval;
    }

    retval = AHT20_Read(buffer, sizeof(buffer));
    if (retval != IOT_SUCCESS) {
        return retval;
    }

    if (AHT20_STATUS_BUSY(buffer[0]) || !AHT20_STATUS_CALI(buffer[0])) {
        retval = AHT20_ResetCommand();
        if (retval != IOT_SUCCESS) {
            return retval;
        }

        usleep(AHT20_STARTUP_TIME);

        retval = AHT20_CalibrateCommand();
        usleep(AHT20_CALIBRATION_TIME);

        return retval;
    }

    return IOT_SUCCESS;
}

/**
 * @brief Retrieve AHT20 measurement results.
 *
 * This function retrieves the temperature and humidity data from the AHT20 sensor.
 * It converts the raw measurement data into standard temperature and humidity values.
 *
 * @param temp Pointer to store the measured temperature.
 * @param humi Pointer to store the measured humidity.
 *
 * @return Returns IOT_SUCCESS if the operation is successful, otherwise returns 
 *         the error code.
 *
 * @author Maker Studio
 * @license Apache 2.0
 */
uint32_t AHT20_GetMeasureResult(float* temp, float* humi)
{
    uint32_t retval = 0, i = 0;

    if (temp == NULL || humi == NULL) {
        return IOT_FAILURE;
    }

    uint8_t buffer[AHT20_STATUS_RESPONSE_MAX] = { 0 };
    memset(&buffer, 0x0, sizeof(buffer));

    retval = AHT20_Read(buffer, sizeof(buffer));
    if (retval != IOT_SUCCESS) {
        return retval;
    }

    for (i = 0; AHT20_STATUS_BUSY(buffer[0]) && i < AHT20_MAX_RETRY; i++) {
        usleep(AHT20_MEASURE_TIME);
        retval = AHT20_Read(buffer, sizeof(buffer));
        if (retval != IOT_SUCCESS) {
            return retval;
        }
    }

    if (i >= AHT20_MAX_RETRY) {
        osal_printk("AHT20 device always busy!\r\n");
        return IOT_FAILURE;
    }

    uint32_t humiRaw = buffer[1];
    humiRaw = (humiRaw << 8) | buffer[2];
    humiRaw = (humiRaw << 4) | ((buffer[3] & 0xF0) >> 4);
    *humi = humiRaw / (float)AHT20_RESLUTION * 100;

    uint32_t tempRaw = buffer[3] & 0x0F;
    tempRaw = (tempRaw << 8) | buffer[4];
    tempRaw = (tempRaw << 8) | buffer[5];
    *temp = tempRaw / (float)AHT20_RESLUTION * 200 - 50;
    // return sucess
    return IOT_SUCCESS;
}